﻿/*            Copyright(c) 2006-2008 Dave Sexton           *
 * http://www.codeplex.com/DocProject                      *
 * http://www.codeplex.com/DocProject/Project/License.aspx *
 ***********************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.Xml;
using System.Data;

namespace DaveSexton.DocProject.Sandcastle
{
	[Serializable]
	[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "x")]
	public sealed class Help2xProject : IDisposable
	{
		#region Public Properties
		public static readonly string DefaultAboutPageIconTopicFile = "AboutPageIcon.htm";
		public static readonly string DefaultAboutPageInfoTopicFile = "AboutPageInfo.htm";
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Nav")]
		public static readonly string DefaultNavFailPageTopicFile = "NavFailPage.htm";
		public static readonly string DefaultEmptyIndexTermTopicFile = "EmptyIndexTerm.htm";

		[Browsable(false)]
		public bool IsDirty { get; private set; }

		[DisplayName("Produce Help 2.x"), Description("Indicates whether the project will produce Help 2.x (.HxS) output.")]
		public bool OutputEnabled
		{
			get
			{
				return IsEnabled(project);
			}
			set
			{
				if (value != OutputEnabled)
				{
					if (value && !Help2CompilerStep.Help2APILoaded)
						throw new InvalidOperationException(Resources.Errors.Help2CompilerNotFound);

					project.Settings.Write("Sandcastle_ProduceHelp2x", value);
				}
			}
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", MessageId = "System.Boolean.TryParse(System.String,System.Boolean@)")]
		private bool ShouldSerializeOutputEnabled()
		{
			bool value;
			bool.TryParse(project.Settings.GetCommittedValue("Sandcastle_ProduceHelp2x"), out value);

			return OutputEnabled != value;
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", MessageId = "System.Boolean.TryParse(System.String,System.Boolean@)")]
		private void ResetOutputEnabled()
		{
			bool value;
			bool.TryParse(project.Settings.GetCommittedValue("Sandcastle_ProduceHelp2x"), out value);

			OutputEnabled = value;
		}

		[DisplayName("Default page"), Description("The topic that is displayed when Document Explorer starts.")]
		public string DefaultPageTopicFile
		{
			get
			{
				EnsureLoaded();
				return defaultPageTopicFile;
			}
			set
			{
				if (DefaultPageTopicFile != value)
				{
					defaultPageTopicFile = value;
					IsDirty = true;
				}
			}
		}

		[DisplayName("Home page"), Description("The topic that is displayed when the Home Page command is invoked.")]
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "HomePage")]
		public string HomePageTopicFile
		{
			get
			{
				EnsureLoaded();
				return homePageTopicFile;
			}
			set
			{
				if (HomePageTopicFile != value)
				{
					homePageTopicFile = value;
					IsDirty = true;
				}
			}
		}

		[Browsable(false)]
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Nav")]
		public string NavFailPageTopicFile
		{
			get
			{
				EnsureLoaded();
				return EnsureAbsolutePath(navFailPageTopicFile ?? DefaultNavFailPageTopicFile);
			}
			set
			{
				if (NavFailPageTopicFile != value)
				{
					navFailPageTopicFile = value;
					IsDirty = true;
				}
			}
		}

		[Browsable(false)]
		public string EmptyIndexTermTopicFile
		{
			get
			{
				EnsureLoaded();
				return EnsureAbsolutePath(emptyIndexTermTopicFile ?? DefaultEmptyIndexTermTopicFile);
			}
			set
			{
				if (EmptyIndexTermTopicFile != value)
				{
					emptyIndexTermTopicFile = value;
					IsDirty = true;
				}
			}
		}

		[Browsable(false)]
		public string AboutPageInfoTopicFile
		{
			get
			{
				EnsureLoaded();
				return EnsureAbsolutePath(aboutPageInfoTopicFile ?? DefaultAboutPageInfoTopicFile);
			}
			set
			{
				if (AboutPageInfoTopicFile != value)
				{
					aboutPageInfoTopicFile = value;
					IsDirty = true;
				}
			}
		}

		[Browsable(false)]
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Nav")]
		public string NavFailPageHtml
		{
			get
			{
				if (navFailPageHtml == null)
					navFailPageHtml = LoadHtml(NavFailPageTopicFile);

				string html = navFailPageHtml;

				if (!string.IsNullOrEmpty(html))
					importer.RebaseImagesToTargetDirectory(ref html);

				return html;
			}
			set
			{
				navFailPageHtml = value ?? string.Empty;
				importNavFailPageHtml = !string.IsNullOrEmpty(value);
				IsDirty = true;
			}
		}

		[Browsable(false)]
		public string EmptyIndexTermHtml
		{
			get
			{
				if (emptyIndexTermHtml == null)
					emptyIndexTermHtml = LoadHtml(EmptyIndexTermTopicFile);

				string html = emptyIndexTermHtml;

				if (!string.IsNullOrEmpty(html))
					importer.RebaseImagesToTargetDirectory(ref html);

				return html;
			}
			set
			{
				emptyIndexTermHtml = value ?? string.Empty;
				importEmptyIndexTermHtml = !string.IsNullOrEmpty(value);
				IsDirty = true;
			}
		}

		[Browsable(false)]
		public string AboutPageInfoHtml
		{
			get
			{
				if (aboutPageInfoHtml == null)
					aboutPageInfoHtml = LoadHtml(AboutPageInfoTopicFile);

				string html = aboutPageInfoHtml;

				if (!string.IsNullOrEmpty(html))
					importer.RebaseImagesToTargetDirectory(ref html);

				return html;
			}
			set
			{
				aboutPageInfoHtml = value ?? string.Empty;
				importAboutPageInfoHtml = !string.IsNullOrEmpty(value);
				IsDirty = true;
			}
		}

		[Browsable(false)]
		public string AboutImageFile
		{
			get
			{
				return aboutImageFile;
			}
		}

		[Browsable(false)]
		public MetadataCollection Attributes
		{
			get
			{
				EnsureLoaded();
				return attributes;
			}
		}

		[Browsable(false)]
		public IDocProject DocProject
		{
			get
			{
				return project;
			}
		}
		#endregion

		#region Private / Protected
		[NonSerialized]
		private readonly IDocProject project;
		private readonly MetadataCollection attributes = new MetadataCollection();
		private readonly string metadataFile, namedUrlIndexFile, msHelp2HtmlDirectory, aboutImageFile, mediaDirectoryPath;
		private string defaultPageTopicFile, homePageTopicFile, navFailPageTopicFile, emptyIndexTermTopicFile, aboutPageInfoTopicFile;
		private string navFailPageHtml, emptyIndexTermHtml, aboutPageInfoHtml;
		private bool loaded, importNavFailPageHtml, importEmptyIndexTermHtml, importAboutPageInfoHtml;
		[NonSerialized]
		private readonly DaveSexton.Controls.Importer importer;
		#endregion

		#region Constructors
		/// <summary>
		/// Constructs a new instance of the <see cref="Help2xProject" /> class.
		/// </summary>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "4#")]
		public Help2xProject(IDocProject project, string mediaDirectoryPath, string mediaDirectoryRelative, 
			string metadataFile, string namedUrlIndexFile, string msHelp2HtmlDirectory, string aboutImageFile)
		{
			if (project == null)
				throw new ArgumentNullException("project");

			if (string.IsNullOrEmpty(metadataFile))
				throw new ArgumentException(Resources.Errors.NullOrEmpty, "metadataFile");

			if (string.IsNullOrEmpty(namedUrlIndexFile))
				throw new ArgumentException(Resources.Errors.NullOrEmpty, "namedUrlIndexFile");

			if (string.IsNullOrEmpty(msHelp2HtmlDirectory))
				throw new ArgumentException(Resources.Errors.NullOrEmpty, "msHelp2HtmlDirectory");

			if (string.IsNullOrEmpty(aboutImageFile))
				throw new ArgumentException(Resources.Errors.NullOrEmpty, "aboutImageFile");

			this.project = project;
			this.metadataFile = metadataFile;
			this.namedUrlIndexFile = namedUrlIndexFile;
			this.msHelp2HtmlDirectory = msHelp2HtmlDirectory;
			this.aboutImageFile = aboutImageFile;
			this.mediaDirectoryPath = mediaDirectoryPath;

			importer = new DaveSexton.Controls.Importer(mediaDirectoryPath);
			importer.Rebase = DaveSexton.Controls.ImporterRebaseAction.RebaseDirectory;
			importer.RebaseDirectory = ".." + PathFormat.MakeVirtual(mediaDirectoryRelative);

			attributes.Changed += (s, e) => IsDirty = true;
		}

		/// <summary>
		/// Constructs a new instance of the <see cref="Help2xProject" /> class.
		/// </summary>
		public Help2xProject(Help2xProject clone, bool cloneIsDirty)
		{
			if (clone == null)
				throw new ArgumentNullException("clone");

			project = clone.project;
			metadataFile = clone.metadataFile;
			namedUrlIndexFile = clone.namedUrlIndexFile;
			msHelp2HtmlDirectory = clone.msHelp2HtmlDirectory;
			aboutImageFile = clone.aboutImageFile;
			mediaDirectoryPath = clone.mediaDirectoryPath;

			attributes.AddRange(clone.attributes);
			attributes.Changed += (s, e) => IsDirty = true;

			loaded = clone.loaded;

			defaultPageTopicFile = clone.defaultPageTopicFile;
			homePageTopicFile = clone.homePageTopicFile;
			navFailPageTopicFile = clone.navFailPageTopicFile;
			emptyIndexTermTopicFile = clone.emptyIndexTermTopicFile;
			aboutPageInfoTopicFile = clone.aboutPageInfoTopicFile;

			navFailPageHtml = clone.navFailPageHtml;
			emptyIndexTermHtml = clone.emptyIndexTermHtml;
			aboutPageInfoHtml = clone.aboutPageInfoHtml;

			importEmptyIndexTermHtml = clone.importEmptyIndexTermHtml;
			importNavFailPageHtml = clone.importNavFailPageHtml;
			importAboutPageInfoHtml = clone.importAboutPageInfoHtml;

			importer = clone.importer;

			if (cloneIsDirty)
				IsDirty = clone.IsDirty;
		}
		#endregion

		#region Methods
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")]
		public static bool IsEnabled(IDocProject project)
		{
			if (project == null)
				throw new ArgumentNullException("project");

			return project.Settings.Read("Sandcastle_ProduceHelp2x", false);
		}

		internal static DataSet CreateAttributesDataSet(MetadataCollection attributes)
		{
			DataSet data = new DataSet();
			data.Locale = System.Globalization.CultureInfo.CurrentCulture;

			DataTable table = data.Tables.Add("Attributes");
			DataColumn nameColumn = table.Columns.Add("Name", typeof(string));
			DataColumn valueColumn = table.Columns.Add("Value", typeof(string));
			table.PrimaryKey = new[] { nameColumn, valueColumn };

			foreach (Metadata attribute in attributes)
				table.Rows.Add(attribute.Name, attribute.Value);

			return data;
		}

		internal static void FillAttributesFromDataSet(DataSet data, MetadataCollection attributes)
		{
			attributes.Clear();

			DataTable table = data.Tables["Attributes"];

			foreach (DataRow row in table.Rows)
			{
				string name = (string) row["Name"];
				string value = (string) row["Value"];

				if (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(value))
					attributes.Add(new Metadata(name, value));
			}
		}

		internal static DataSet CreateKeywordsDataSet(KeywordMultiCollection keywords)
		{
			DataSet data = new DataSet();
			data.Locale = System.Globalization.CultureInfo.CurrentCulture;

			DataTable keywordsTable = data.Tables.Add("Keywords");
			DataColumn idColumn = keywordsTable.Columns.Add("ID", typeof(int));
			idColumn.AllowDBNull = false;
			idColumn.AutoIncrement = true;
			keywordsTable.PrimaryKey = new[] { idColumn };
			DataColumn indexColumn = keywordsTable.Columns.Add("Index", typeof(string));
			DataColumn keywordColumn = keywordsTable.Columns.Add("Term", typeof(string));
			keywordsTable.Constraints.Add("UniqueKeywords", new[] { indexColumn, keywordColumn }, false);

			DataTable nestedKeywordsTable = data.Tables.Add("NestedKeywords");
			DataColumn parentIdColumn = nestedKeywordsTable.Columns.Add("ParentID", typeof(int));
			DataColumn nestedTermColumn = nestedKeywordsTable.Columns.Add("Term", typeof(string));
			nestedKeywordsTable.Constraints.Add("UniqueNestedKeywords", new[] { parentIdColumn, nestedTermColumn }, false);

			data.Relations.Add("KeywordsRelation", idColumn, parentIdColumn, true);

			foreach (Keyword keyword in keywords.Keys)
			{
				DataRow row = keywordsTable.NewRow();
				row["Index"] = GetIndexTitle(keyword.Index);
				row["Term"] = keyword.Term;
				keywordsTable.Rows.Add(row);

				foreach (Keyword nestedKeyword in keywords[keyword])
					nestedKeywordsTable.Rows.Add(row["ID"], nestedKeyword.Term);
			}

			return data;
		}

		internal static void FillKeywordsFromDataSet(DataSet data, KeywordMultiCollection keywords)
		{
			keywords.Clear();

			DataTable keywordsTable = data.Tables["Keywords"];

			foreach (DataRow row in keywordsTable.Rows)
			{
				string index = GetIndexName((string) row["Index"]);

				KeywordCollection keyword = new KeywordCollection(new Keyword(index, (string) row["Term"]));

				foreach (DataRow nestedRow in row.GetChildRows("KeywordsRelation"))
					keyword.Add(new Keyword(index, (string) nestedRow["Term"]));

				keywords.Add(keyword);
			}
		}

		internal static string GetIndexTitle(string indexName)
		{
			switch (indexName)
			{
				case "K":
					return "Keyword (K)";
				case "F":
					return "Context-sensitive (F)";
				case "S":
					return "Search Window (S)";
				case "B":
					return "Dynamic Link (B)";
				default:
					return null;
			}
		}

		internal static string GetIndexName(string indexTitle)
		{
			switch (indexTitle)
			{
				case "Keyword (K)":
					return "K";
				case "Context-sensitive (F)":
					return "F";
				case "Search Window (S)":
					return "S";
				case "Dynamic Link (B)":
					return "B";
				default:
					return null;
			}
		}

		[System.Diagnostics.DebuggerHidden]
		private static string LoadHtml(string file)
		{
			try
			{
				return System.IO.File.ReadAllText(file);
			}
			catch (System.IO.FileNotFoundException) { }
			catch (System.IO.DirectoryNotFoundException) { }

			return string.Empty;
		}

		private void EnsureLoaded()
		{
			if (!loaded)
			{
				Load();
				loaded = true;
			}
		}

		private string EnsureAbsolutePath(string file)
		{
			if (System.IO.Path.IsPathRooted(file))
				return file;
			else
				return System.IO.Path.Combine(msHelp2HtmlDirectory, file);
		}

		private void Load()
		{
			LoadMetadata();
			LoadNamedUrlIndex();
		}

		[System.Diagnostics.DebuggerHidden]
		private void LoadMetadata()
		{
			try
			{
				TopicManagement.TopicMetadataXml metadataXml = new TopicManagement.TopicMetadataXml(metadataFile, false, true);
				TopicManagement.TopicMetadataCollection metadata = metadataXml.ReadAll();

				if (metadata.Contains("*"))
					attributes.AddRange(metadata["*"].Attributes);
			}
			catch (System.IO.FileNotFoundException) { }
			catch (System.IO.DirectoryNotFoundException) { }
		}

		[System.Diagnostics.DebuggerHidden]
		private void LoadNamedUrlIndex()
		{
			XmlReaderSettings readerSettings = new XmlReaderSettings();
			readerSettings.IgnoreComments = true;
			readerSettings.IgnoreProcessingInstructions = true;
			readerSettings.IgnoreWhitespace = true;
			readerSettings.ProhibitDtd = false;
			readerSettings.XmlResolver = null;		// ignore DTD

			try
			{
				using (XmlReader reader = XmlReader.Create(namedUrlIndexFile, readerSettings))
				{
					if (reader.ReadToFollowing("HelpIndex"))
					{
						while (reader.Read())
						{
							if (reader.NodeType == XmlNodeType.Element && reader.Depth == 1)
							{
								if (reader.LocalName == "Keyword")
								{
									string term = reader.GetAttribute("Term");

									if (!string.IsNullOrEmpty(term) && reader.ReadToDescendant("Jump"))
									{
										string url = reader.GetAttribute("Url");

										if (!string.IsNullOrEmpty(url))
										{
											string fileName = System.IO.Path.GetFileName(url);

											switch (term)
											{
												case "AboutPageInfo":
													aboutPageInfoTopicFile = fileName;
													break;
												case "DefaultPage":
													defaultPageTopicFile = fileName;
													break;
												case "HomePage":
													homePageTopicFile = fileName;
													break;
												case "NavFailPage":
													navFailPageTopicFile = fileName;
													break;
												case "EmptyIndexTerm":
													emptyIndexTermTopicFile = fileName;
													break;
											}
										}
									}
								}
							}
						}
					}
				}
			}
			catch (System.IO.FileNotFoundException) { }
			catch (System.IO.DirectoryNotFoundException) { }
		}

		public void Save()
		{
			if (IsDirty)
			{
				if (loaded)
				{
					int importedImagesCount = 0;

					if (importAboutPageInfoHtml)
						importedImagesCount += importer.ImportImages(ref aboutPageInfoHtml);

					if (importEmptyIndexTermHtml)
						importedImagesCount += importer.ImportImages(ref emptyIndexTermHtml);

					if (importNavFailPageHtml)
						importedImagesCount += importer.ImportImages(ref navFailPageHtml);

					if (importedImagesCount > 0)
						project.GetItem(mediaDirectoryPath).Refresh();

					SaveMetadata();
					SaveNamedUrlIndex();
				}

				IsDirty = false;
			}
		}

		private void SaveMetadata()
		{
			IProjectItem item = project.GetItem(metadataFile);
			item.Checkout();

			TopicManagement.TopicMetadataXml metadataXml = new TopicManagement.TopicMetadataXml(metadataFile);
			metadataXml.Write(new TopicManagement.TopicMetadata("*", null, null, null, attributes));

			item.Show();
		}

		private void SaveNamedUrlIndex()
		{
			IProjectItem htmlFolder = project.GetFolder(msHelp2HtmlDirectory);
			htmlFolder.Show();

			IProjectItem indexFile = project.GetItem(namedUrlIndexFile);
			indexFile.Checkout();

			XmlDocument document = new XmlDocument();
			System.IO.Stream stream = null;

			try
			{
				if (indexFile.Exists)
				{
					try
					{
						stream = System.IO.File.OpenRead(namedUrlIndexFile);
					}
					catch (System.IO.FileNotFoundException) { }
					catch (System.IO.DirectoryNotFoundException) { }
				}

				if (stream == null)
					stream = new System.IO.MemoryStream(Encoding.UTF8.GetBytes(Resources.MSHelpV2.index_n));

				LoadNamedUrlIndexFileResource(document, stream);
			}
			finally
			{
				stream.Close();
			}

			bool aboutPageIconTopicFileRequired = System.IO.File.Exists(aboutImageFile);
			string aboutPageIconFile = EnsureAbsolutePath(DefaultAboutPageIconTopicFile);

			SaveKeyword(document, "AboutPageIcon", (aboutPageIconTopicFileRequired) ? aboutPageIconFile : null);
			SaveKeyword(document, "DefaultPage", defaultPageTopicFile);
			SaveKeyword(document, "HomePage", homePageTopicFile);

			if (aboutPageIconTopicFileRequired)
				CreateSpecialPage(aboutPageIconFile, Resources.MSHelpV2.AboutPageIcon, System.IO.Path.GetFileName(aboutImageFile));

			SaveSpecialPage(document, "AboutPageInfo", AboutPageInfoTopicFile, Resources.MSHelpV2.AboutPageInfo, aboutPageInfoHtml);
			SaveSpecialPage(document, "NavFailPage", NavFailPageTopicFile, Resources.MSHelpV2.NavFailPage, navFailPageHtml);
			SaveSpecialPage(document, "EmptyIndexTerm", EmptyIndexTermTopicFile, Resources.MSHelpV2.EmptyIndexTerm, emptyIndexTermHtml);

			document.Save(namedUrlIndexFile);

			indexFile.Show();
		}

		private static void LoadNamedUrlIndexFileResource(XmlDocument document, System.IO.Stream stream)
		{
			XmlReaderSettings settings = new XmlReaderSettings();
			settings.XmlResolver = null;
			settings.ProhibitDtd = false;

			using (XmlReader reader = XmlReader.Create(stream, settings))
			{
				document.Load(reader);
			}
		}

		private static void SaveKeyword(XmlDocument document, string term, string file)
		{
			if (string.IsNullOrEmpty(file))
				RemoveKeyword(document, term);
			else
				AddKeyword(document, term, file);
		}

		private static void AddKeyword(XmlDocument document, string term, string file)
		{
			XmlNode keyword = document.DocumentElement.SelectSingleNode("Keyword[@Term='" + term + "']");

			if (keyword == null)
				keyword = document.DocumentElement.AppendChild(document.CreateElement("Keyword"));

			XmlAttribute termAttribute = document.CreateAttribute("Term");
			termAttribute.Value = term;
			keyword.Attributes.SetNamedItem(termAttribute);

			XmlNode jump = keyword.SelectSingleNode("Jump");

			if (jump == null)
				jump = keyword.AppendChild(document.CreateElement("Jump"));

			XmlAttribute urlAttribute = document.CreateAttribute("Url");
			urlAttribute.Value = "html2/" + System.IO.Path.GetFileName(file);
			jump.Attributes.SetNamedItem(urlAttribute);
		}

		private static void RemoveKeyword(XmlDocument document, string term)
		{
			XmlNode keyword = document.DocumentElement.SelectSingleNode("Keyword[@Term='" + term + "']");

			if (keyword != null)
				keyword.ParentNode.RemoveChild(keyword);
		}

		private void CreateSpecialPage(string file, string template, string content)
		{
			if (string.IsNullOrEmpty(content))
				return;

			IProjectItem item = project.GetItem(file);
			item.Checkout();

			if (template != null)
				content = template.Replace("$Content$", content);

			System.IO.File.WriteAllText(file, content, Encoding.UTF8);

			item.Show();
		}

		private void SaveSpecialPage(XmlDocument document, string term, string file, string template, string html)
		{
			if (string.IsNullOrEmpty(html))
			{
				project.GetItem(file).Delete();
				SaveKeyword(document, term, null);
			}
			else
			{
				CreateSpecialPage(file, template, html);
				SaveKeyword(document, term, file);
			}
		}

		public void ImportAboutPageImage(string file)
		{
			if (string.IsNullOrEmpty(file))
				throw new ArgumentException(Resources.Errors.NullOrEmpty, "file");

			using (System.Drawing.Bitmap bitmap = (System.Drawing.Bitmap) System.Drawing.Bitmap.FromFile(file))
			{
				IProjectItem item = project.GetItem(aboutImageFile);
				item.Checkout();

				bitmap.Save(aboutImageFile, System.Drawing.Imaging.ImageFormat.Jpeg);

				item.Show();

				// The project must be marked as dirty so that saving it will ensure that the named URL index file exists and has an AboutPageIcon entry.
				IsDirty = true;
			}
		}
		#endregion

		#region IDisposable Members
		/// <summary>
		/// Releases all resources used by an instance of the <see cref="Help2xProject" /> class.
		/// </summary>
		/// <remarks>
		/// This method calls the virtual <see cref="Dispose(bool)" /> method, passing in <strong>true</strong>, and then suppresses 
		/// finalization of the instance.
		/// </remarks>
		public void Dispose()
		{
			Dispose(true);
			GC.SuppressFinalize(this);
		}

		/// <summary>
		/// Releases unmanaged resources before an instance of the <see cref="Help2xProject" /> class is reclaimed by garbage collection.
		/// </summary>
		/// <remarks>
		/// This method releases unmanaged resources by calling the virtual <see cref="Dispose(bool)" /> method, passing in <strong>false</strong>.
		/// </remarks>
		~Help2xProject()
		{
			Dispose(false);
		}

		/// <summary>
		/// Releases the unmanaged resources used by an instance of the <see cref="Help2xProject" /> class and optionally releases the managed resources.
		/// </summary>
		/// <param name="disposing"><strong>true</strong> to release both managed and unmanaged resources; <strong>false</strong> to release only unmanaged resources.</param>
		private void Dispose(bool disposing)
		{
			if (disposing)
			{
				if (importer != null)
					importer.Dispose();
			}
		}
		#endregion
	}
}
